

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using vnFingerPrint.SDK.Library;

namespace vnBiometricsSDK.FingerPrint
{
    /// <summary>
    /// Summary description for CFinger.
    /// </summary>

    public class CFingerPrint
    {
        //used for image
        //for digital persona with kit
        //public int FP_IMAGE_WIDTH = 500;
        //public int FP_IMAGE_HEIGHT = 500;
        //for verifinger with kit  
        public int FP_IMAGE_WIDTH = 512;
        public int FP_IMAGE_HEIGHT = 512;
        //Used by template
        //be carefull the size of the array must always be 1 larger than a number divisable by 4
        public int FP_TEMPLATE_MAX_SIZE = 1801;
        //used for matching
        //the max distance between to points when comparing two points to count as a match
        public int FP_MATCH_POINT_DISTANCE_MOVEMENT = 10;
        //the max rotation to use when comparint two points to count as a match
        public int FP_MATCH_POINT_ROTATION_MOVEMENT = 10;//10;
        //a percentage
        public int FP_MATCH_THRESHOLD = 55;
        //finger print classifications
        //Wirbel class
        public const int FP_CLASS_WHORL = 1;
        //lasso class
        public const int FP_CLASS_LEFT_LOOP = 2;
        public const int FP_CLASS_RIGHT_LOOP = 3;
        public const int FP_CLASS_ARCH = 4;
        public const int FP_CLASS_ARCH_TENTED = 5;
        //fingerprint template values
        private const int FP_TEMPLATE_SIZE = 0;
        private const int FP_TEMPLATE_ORIGIN_X = 1;
        private const int FP_TEMPLATE_ORIGIN_Y = 2;
        private const int FP_TEMPLATE_FEATURE_SIZE = 6;
        private const int FP_TEMPLATE_SEARCH_RADIUS = 1;
        //fingerprint origin values 
        //private int FP_ORIGIN_SEARCH_RADIUS = 5;
        //holds skeletinized image
        public byte[,] P = new byte[512, 512];
        public double[,] Direct = new double[512, 512];
        public Point Core, DeltaR, DeltaL;
        private double[,] GetDirectionMatrix(int widthSqare)
        {
            int i, j, x, y;
            int Ax, Ay, Axy;
            int Gxx, Gyy, Gxy;
            int Bx, By;

            double[,] directMatrix = new double[512, 512];
            for (j = 0; j < 512; j++)
                for (i = 0; i < 512; i++)
                    directMatrix[i, j] = 0;
            for (y = widthSqare + 1; y < 512 - widthSqare - 1; y++)
            {
                for (x = widthSqare + 1; x < 512 - widthSqare - 1; x++)
                {
                    Ax = 0; Ay = 0; Axy = 0;
                    Gxx = 0; Gyy = 0; Gxy = 0;
                    Bx = 0; By = 0;
                    for (j = y - widthSqare; j < y + widthSqare - 1; j++)
                    {
                        for (i = x - widthSqare; i < x + widthSqare - 1; i++)
                        {
                            Bx = ((P[i + 2, j] + 2 * P[i + 2, j + 1] + P[i + 2, j + 2] - P[i, j] - 2 * P[i, j + 1] - P[i, j + 2]));
                            By = ((P[i, j + 2] + 2 * P[i + 1, j + 2] + P[i + 2, j + 2] - P[i, j] - 2 * P[i + 1, j] - P[i + 2, j]));
                            Ax += Bx * Bx;
                            Ay += By * By;
                            Axy += Bx * By;
                        }
                    }
                    Gxx = Ax;
                    Gyy = Ay;
                    Gxy = Axy;

                    directMatrix[x, y] = Math.PI / 2 - 0.5 * Math.Atan2(2 * Gxy, Gxx - Gyy);
                }
            }
            return directMatrix;

        }
        public CFingerPrint()
        {
        }

        public CFingerPrint(int width, int height)
        {
            FP_IMAGE_WIDTH = width;
            FP_IMAGE_HEIGHT = height;
            P = new byte[width, height];
        }

        public CFingerPrint(int width, int height, int MatchPointDistanceMovement, int MatchPointRotationMovment, int MatchThreshold)
        {

            FP_IMAGE_WIDTH = width;
            FP_IMAGE_HEIGHT = height;
            FP_MATCH_POINT_DISTANCE_MOVEMENT = MatchPointDistanceMovement;
            FP_MATCH_POINT_ROTATION_MOVEMENT = MatchPointRotationMovment;
            FP_MATCH_THRESHOLD = MatchThreshold;
        }

        public void setFingerPrintImage(Bitmap m_image)
        {
            try
            {
                for (int i = 0; i <= FP_IMAGE_WIDTH - 1; i++)
                {
                    for (int j = 0; j <= FP_IMAGE_HEIGHT - 1; j++)
                    {
                        if ((m_image.GetPixel(i, j).B <= 127) && (m_image.GetPixel(i, j).R <= 127) && (m_image.GetPixel(i, j).G <= 127))
                        {
                            P[i, j] = 1;
                        }
                        else
                        {
                            P[i, j] = 0;
                        }

                    }
                }
                //set edges to 0
                for (int i = 0; i <= FP_IMAGE_WIDTH - 1; i++)
                {
                    P[i, 0] = 0;
                    P[i, FP_IMAGE_HEIGHT - 1] = 0;
                }
                for (int j = 0; j <= FP_IMAGE_HEIGHT - 1; j++)
                {
                    P[0, j] = 0;
                    P[FP_IMAGE_WIDTH - 1, j] = 0;
                }
                Direct = GetDirectionMatrix(4);
                FindSingularrity(45, 25);
            }
            catch (Exception ex)
            {
                // throw ex;
            }
        }
        public Bitmap getFingerPrintImage()
        {
            Bitmap imageBuffer = new Bitmap(FP_IMAGE_WIDTH, FP_IMAGE_HEIGHT);
            for (int i = 0; i <= FP_IMAGE_WIDTH - 1; i++)
            {
                for (int j = 0; j <= FP_IMAGE_HEIGHT - 1; j++)
                {
                    if (P[i, j] == 1)
                    {
                        imageBuffer.SetPixel(i, j, Color.Blue);
                    }
                    else
                    {
                        imageBuffer.SetPixel(i, j, Color.White);
                    }
                }
            }
            return imageBuffer;
        }

        public Bitmap getFingerPrintImageDetail()
        {
            //set finger print image
            Bitmap imageBuffer = new Bitmap(FP_IMAGE_WIDTH, FP_IMAGE_HEIGHT);
            for (int i = 0; i <= FP_IMAGE_WIDTH - 1; i++)
            {
                for (int j = 0; j <= FP_IMAGE_HEIGHT - 1; j++)
                {
                    if (P[i, j] == 1)
                        imageBuffer.SetPixel(i, j, Color.Blue);
                    else
                        imageBuffer.SetPixel(i, j, Color.White);
                }
            }


            //get features
            double[] m_arr = this.getFingerPrintTemplate();
            //	int linelength = 5;
            //draw points
            Pen penBlue = new Pen(Color.Blue, 1);
            Pen penRed = new Pen(Color.Red, 1);
            Pen penGreen = new Pen(Color.Green, 1);
            Pen penGray = new Pen(Color.Gray, 1);
            //Graphics gf = Graphics.FromImage(Image.FromHbitmap(m_ImageBuffer.GetHbitmap() ));
            Graphics gf = Graphics.FromImage(imageBuffer);
            gf.CompositingMode = CompositingMode.SourceOver;
            for (int i = 7; i <= m_arr[0] - 1; i = i + 6)
            {
                if (m_arr[i + 4] > 1)
                {
                    gf.DrawRectangle(penRed, (int)m_arr[i] + (int)m_arr[1] - 3, (int)m_arr[i + 1] + (int)m_arr[2] - 2, 5, 5);
                }
                else if (m_arr[i + 4] == 1)
                {
                    gf.DrawEllipse(penGreen, (int)m_arr[i] + (int)m_arr[1] - 3, (int)m_arr[i + 1] + (int)m_arr[2] - 2, 5, 5);
                }


            }//end for
            //draws the origin
            gf.DrawLine(penGray, (int)m_arr[1] - 5, (int)m_arr[2], (int)m_arr[1] + 5, (int)m_arr[2]);
            gf.DrawLine(penGray, (int)m_arr[1], (int)m_arr[2] - 5, (int)m_arr[1], (int)m_arr[2] + 5);
            return imageBuffer;
        }//end getFingerPrintImageDetail()
        public Bitmap getFingerPrintImageDetail(Color cl, bool drawMinutae)
        {
            //set finger print image
            Bitmap imageBuffer = new Bitmap(FP_IMAGE_WIDTH, FP_IMAGE_HEIGHT);
            for (int i = 0; i <= FP_IMAGE_WIDTH - 1; i++)
            {
                for (int j = 0; j <= FP_IMAGE_HEIGHT - 1; j++)
                {
                    if (P[i, j] == 1)
                        imageBuffer.SetPixel(i, j, cl);
                    else
                        imageBuffer.SetPixel(i, j, Color.White);
                }
            }

            //get features
            double[] m_arr = this.getFingerPrintTemplate();
            //	int linelength = 5;
            //draw points
            Pen penBlue = new Pen(Color.Blue, 1);
            SolidBrush brushGreen = new SolidBrush(Color.Green);
            SolidBrush brushRed = new SolidBrush(Color.Red);
            SolidBrush brushViolet = new SolidBrush(Color.DarkViolet);
            Pen penRed = new Pen(Color.Red, 1);
            Pen penGreen = new Pen(Color.Green, 1);
            Pen penGray = new Pen(Color.Gray, 1);
            //Graphics gf = Graphics.FromImage(Image.FromHbitmap(m_ImageBuffer.GetHbitmap() ));
            Graphics gf = Graphics.FromImage(imageBuffer);
            gf.CompositingMode = CompositingMode.SourceOver;
            if (drawMinutae)
                for (int i = 7; i <= m_arr[0] - 1; i = i + 6)
                {
                    if (m_arr[i + 4] > 1)
                    {
                        gf.FillRectangle(brushRed, (int)m_arr[i] + (int)m_arr[1] - 3, (int)m_arr[i + 1] + (int)m_arr[2] - 2, 5, 5);
                    }
                    else if (m_arr[i + 4] == 1)
                    {
                        gf.FillEllipse(brushGreen, (int)m_arr[i] + (int)m_arr[1] - 3, (int)m_arr[i + 1] + (int)m_arr[2] - 2, 5, 5);
                    }
                    //gf.DrawLine(penRed, (int)m_arr[i] + (int)m_arr[1], (int)m_arr[i + 1] + (int)m_arr[2],
                    //    (int)m_arr[i] + (int)m_arr[1] + Convert.ToInt32(15 * Math.Cos(Direct[(int)m_arr[i] + (int)m_arr[1],(int)m_arr[i + 1] + (int)m_arr[2]]))
                    //    , (int)m_arr[i + 1] + (int)m_arr[2] - Convert.ToInt32(15 * Math.Sin(Direct[(int)m_arr[i] + (int)m_arr[1],(int)m_arr[i + 1] + (int)m_arr[2]])));

                }//end for
            gf.FillEllipse(brushViolet, Core.X, Core.Y, 10, 10);
            Font f = new Font(FontFamily.GenericSansSerif, 20f);
            gf.DrawString("C", f, brushRed, new PointF(Core.X, Core.Y));
            gf.FillEllipse(brushViolet, DeltaR.X + Core.X, DeltaR.Y + Core.Y, 10, 10);
            gf.DrawString("R", f, brushRed, new PointF(DeltaR.X + Core.X, DeltaR.Y + Core.Y));
            gf.FillEllipse(brushViolet, DeltaL.X + Core.X, DeltaL.Y + Core.Y, 10, 10);
            gf.DrawString("L", f, brushRed, new PointF(DeltaL.X + Core.X, DeltaL.Y + Core.Y));

            ////test huong
            //for (int i = 0; i < 512; i++)
            //{
            //    for (int j = 0; j < 512; j++)
            //    {
            //        if (P[i,j]>0&&i%15==0)
            //        {
            //            gf.DrawLine(penRed, i,j, i + Convert.ToInt32(15 * Math.Cos(Direct[i, j])), j - Convert.ToInt32(15 * Math.Sin(Direct[i, j])));
            //        }
            //    }
            //}
            return imageBuffer;
        }
        public Bitmap getFingerPrintImageDetailInputANN(Color cl)
        {
            //set finger print image
            Bitmap imageBuffer = new Bitmap(FP_IMAGE_WIDTH, FP_IMAGE_HEIGHT);
            for (int i = 0; i <= FP_IMAGE_WIDTH - 1; i++)
            {
                for (int j = 0; j <= FP_IMAGE_HEIGHT - 1; j++)
                {
                    if (P[i, j] == 1)
                        imageBuffer.SetPixel(i, j, cl);
                    else
                        imageBuffer.SetPixel(i, j, Color.White);
                }
            }

            //get features
            double[] m_arr = this.getFingerPrintTemplate();
            //	int linelength = 5;
            //draw points
            Pen penBlue = new Pen(Color.Blue, 1);
            SolidBrush brushGreen = new SolidBrush(Color.Green);
            SolidBrush brushRed = new SolidBrush(Color.Red);
            SolidBrush brushViolet = new SolidBrush(Color.DarkViolet);
            Pen penRed = new Pen(Color.Red, 1);
            Pen penGreen = new Pen(Color.Green, 1);
            Pen penGray = new Pen(Color.Gray, 1);
            //Graphics gf = Graphics.FromImage(Image.FromHbitmap(m_ImageBuffer.GetHbitmap() ));
            Graphics gf = Graphics.FromImage(imageBuffer);
            gf.CompositingMode = CompositingMode.SourceOver;
            string s = ConvertFingerPrintTemplateDoubleToString(getFingerPrintTemplate());
            float[] x = Helper.InputANN("0;0;0;0;0;0", s);
            gf.FillEllipse(brushViolet, Core.X, Core.Y, 10, 10);
            gf.FillRectangle(brushRed, (int)x[4] + Core.X - 3, (int)x[5] + Core.Y - 3, 5, 5);
            gf.FillRectangle(brushRed, (int)x[7] + Core.X - 3, (int)x[8] + Core.Y - 3, 5, 5);
            gf.FillRectangle(brushRed, (int)x[10] + Core.X - 3, (int)x[11] + Core.Y - 3, 5, 5);
            gf.FillRectangle(brushRed, (int)x[13] + Core.X - 3, (int)x[14] + Core.Y - 3, 5, 5);

            Font f = new Font(FontFamily.GenericSansSerif, 20f);
            gf.DrawString("C", f, brushRed, new PointF(Core.X, Core.Y));
            gf.FillEllipse(brushViolet, DeltaR.X + Core.X, DeltaR.Y + Core.Y, 10, 10);
            gf.DrawString("R", f, brushRed, new PointF(DeltaR.X + Core.X, DeltaR.Y + Core.Y));
            gf.FillEllipse(brushViolet, DeltaL.X + Core.X, DeltaL.Y + Core.Y, 10, 10);
            gf.DrawString("L", f, brushRed, new PointF(DeltaL.X + Core.X, DeltaL.Y + Core.Y));

            ////test huong
            //for (int i = 0; i < 512; i++)
            //{
            //    for (int j = 0; j < 512; j++)
            //    {
            //        if (P[i,j]>0&&i%15==0)
            //        {
            //            gf.DrawLine(penRed, i,j, i + Convert.ToInt32(15 * Math.Cos(Direct[i, j])), j - Convert.ToInt32(15 * Math.Sin(Direct[i, j])));
            //        }
            //    }
            //}
            return imageBuffer;
        }
        public void ThinningHilditch()
        {
            int change = 1;
            bool mbool = true;
            while (change != 0)
            {
                change = 0;
                for (int i = 2; i <= FP_IMAGE_WIDTH - 2; i++)
                {
                    for (int j = 2; j <= FP_IMAGE_HEIGHT - 2; j++)
                    {
                        if (P[i, j] == 1)
                        {
                            short c = 0;
                            //count surrounding 1
                            //a) Make sure pixel 1, has 2 to 6 (inclusive) neighbors
                            if (P[i, j + 1] == 1) { c++; }
                            if (P[i + 1, j + 1] == 1) { c++; }
                            if (P[i + 1, j] == 1) { c++; }
                            if (P[i + 1, j - 1] == 1) { c++; }
                            if (P[i, j - 1] == 1) { c++; }
                            if (P[i - 1, j - 1] == 1) { c++; }
                            if (P[i - 1, j] == 1) { c++; }
                            if (P[i - 1, j + 1] == 1) { c++; }

                            if ((c >= 2) && (c <= 6))
                            {
                                c = 0;
                                //b) starting from 2, go clockwise until 9, and count the
                                //'   number of 0 to 1 transitions.  This should be equal to 1.
                                if ((P[i - 1, j + 1] == 0) && (P[i, j + 1] == 1)) { c++; }
                                if ((P[i, j + 1] == 0) && (P[i + 1, j + 1] == 1)) { c++; }
                                if ((P[i + 1, j + 1] == 0) && (P[i + 1, j] == 1)) { c++; }
                                if ((P[i + 1, j] == 0) && (P[i + 1, j - 1] == 1)) { c++; }
                                if ((P[i + 1, j - 1] == 0) && (P[i, j - 1] == 1)) { c++; }
                                if ((P[i, j - 1] == 0) && (P[i - 1, j - 1] == 1)) { c++; }
                                if ((P[i - 1, j - 1] == 0) && (P[i - 1, j] == 1)) { c++; }
                                if ((P[i - 1, j] == 0) && (P[i - 1, j + 1] == 1)) { c++; }

                                if (c == 1)
                                {
                                    c = 0;
                                    if (mbool == true)
                                    {
                                        //c) 2*4*6=0  (ie either 2,4 ,or 6 is off)
                                        if ((P[i, j + 1] * P[i + 1, j] * P[i + 1, j - 1]) == 0)
                                        {
                                            //d) 4*6*8=0
                                            if ((P[i + 1, j] * P[i + 1, j - 1] * P[i - 1, j]) == 0)
                                            {
                                                P[i, j] = 0;
                                                change++;
                                            }
                                        }
                                        mbool = false;
                                    }
                                    else
                                    {
                                        //c) 2*6*8=0
                                        if ((P[i, j + 1] * P[i + 1, j - 1] * P[i - 1, j]) == 0)
                                        {
                                            //d) 2*4*8=0
                                            if ((P[i, j + 1] * P[i + 1, j] * P[i - 1, j]) == 0)
                                            {
                                                P[i, j] = 0;
                                                change++;
                                            }
                                        }
                                        mbool = true;
                                    }
                                }
                            }
                        }
                    }
                }
            }//End While
        }//end ThinningHilditchAlgorithim

        public void ThinningHitAndMiss()
        {
            /*
            *    basicly you take all patterns
            *    111    X1X
            *    X1X or x11 so on
            *    000    xxX
            *    if these conditions are true then set the middle 1 to 0
            */
            int c = 1;
            while (c != 0)
            {
                c = 0;
                for (int i = 1; i <= FP_IMAGE_WIDTH - 1; i++)
                {
                    for (int j = 1; j <= FP_IMAGE_HEIGHT - 1; j++)
                    {
                        if ((P[i, j] == 1) && (i != 0) && (j != FP_IMAGE_HEIGHT - 1) && (j != 0) && (i != FP_IMAGE_WIDTH - 1))
                        {
                            if ((P[i - 1, j - 1] == 1) && (P[i, j - 1] == 1) && (P[i + 1, j - 1] == 1) && (P[i - 1, j + 1] == 0) && (P[i, j + 1] == 0) && (P[i + 1, j + 1] == 0))
                            {
                                P[i, j] = 0; //'1 on bottom
                                c++;
                            }
                            else if ((P[i - 1, j + 1] == 1) && (P[i, j + 1] == 1) && (P[i + 1, j + 1] == 1) && (P[i - 1, j - 1] == 0) && (P[i, j - 1] == 0) && (P[i + 1, j - 1] == 0))
                            {
                                P[i, j] = 0; //'1 on top
                                c++;
                            }
                            else if ((P[i - 1, j] == 1) && (P[i - 1, j - 1] == 1) && (P[i - 1, j + 1] == 1) && (P[i + 1, j] == 0) && (P[i + 1, j + 1] == 0) && (P[i + 1, j - 1] == 0))
                            {
                                P[i, j] = 0; //'1 on left
                                c++;
                            }
                            else if ((P[i + 1, j] == 1) && (P[i + 1, j - 1] == 1) && (P[i + 1, j + 1] == 1) && (P[i - 1, j] == 0) && (P[i - 1, j + 1] == 0) && (P[i - 1, j - 1] == 0))
                            {
                                P[i, j] = 0; //'1 on right
                                c++;
                            }
                            else if ((P[i - 1, j] == 1) && (P[i, j - 1] == 1) && (P[i, j + 1] == 0) && (P[i + 1, j + 1] == 0) && (P[i + 1, j] == 0))
                            {
                                //x00
                                //110
                                //x1x
                                P[i, j] = 0; //'1 on Bottem Left
                                c++;
                            }
                            else if ((P[i - 1, j] == 1) && (P[i, j + 1] == 1) && (P[i, j - 1] == 0) && (P[i + 1, j - 1] == 0) && (P[i + 1, j] == 0))
                            {
                                //x1x
                                //110
                                //x00
                                P[i, j] = 0; //'1 on Top Left
                                c++;
                            }
                            else if ((P[i, j + 1] == 1) && (P[i + 1, j] == 1) && (P[i - 1, j] == 0) && (P[i - 1, j - 1] == 0) && (P[i, j - 1] == 0))
                            {
                                //x1x
                                //011
                                //00x
                                P[i, j] = 0; //'1 on Top Right
                                c++;
                            }
                            else if ((P[i, j - 1] == 1) && (P[i + 1, j] == 1) && (P[i - 1, j] == 0) && (P[i - 1, j + 1] == 0) && (P[i, j + 1] == 0))
                            {
                                //00x
                                //011
                                //x1x
                                P[i, j] = 0; //'1 on Bottom Right
                                c++;
                            }
                        }
                    }//Next
                }//Next
            }//End While
        }//end ThinningHitAndMiss

        public void ChaneLinkAlgorithm(int ChainLinkDistance)
        {
            //short count1;
            for (int i = 1; i <= FP_IMAGE_WIDTH - 1; i++)
            {
                for (int j = 1; j <= FP_IMAGE_HEIGHT - 1; j++)
                {
                    //change second condition when changeing direction
                    //Horizontal
                    if ((P[i, j] == 1) && (i != FP_IMAGE_WIDTH - 1) && (i != 0) && (j != FP_IMAGE_HEIGHT - 1) && (j != 0))
                    {
                        if (P[i + 1, j] == 0)
                        {
                            short countX = 0;
                            //count Horizontal Hole
                            while (((i + countX) <= FP_IMAGE_WIDTH - 1) && (countX <= ChainLinkDistance))
                            {
                                if (((i + countX + 1) <= FP_IMAGE_WIDTH - 1) && ((countX + 1) <= ChainLinkDistance))
                                {
                                    if (P[i + countX + 1, j] == 0)
                                    {
                                        countX++;
                                    }
                                    else
                                    {
                                        break;
                                    }
                                }
                                else
                                {
                                    break;
                                }

                            }
                            //Fill hole if it is wide enough
                            if ((countX != 0) && ((countX + 1) <= ChainLinkDistance))
                            {
                                for (int temp = 0; temp <= countX; temp++)
                                {
                                    P[i + temp, j] = 1;
                                }
                            }
                        }
                    }
                    //change second condition when changeing direction
                    //Vertical
                    if ((P[i, j] == 1) && (i != FP_IMAGE_WIDTH - 1) && (i != 0) && (j != FP_IMAGE_HEIGHT - 1) && (j != 0))
                    {
                        if (P[i, j + 1] == 0)
                        {
                            short countY = 0;
                            //count Horizontal Hole
                            while (((j + countY) <= FP_IMAGE_HEIGHT - 1) && (countY <= ChainLinkDistance))
                            {
                                if (((j + countY + 1) <= FP_IMAGE_HEIGHT - 1) && ((countY + 1) <= ChainLinkDistance))
                                {
                                    //i pu this here bacause it kept on crashing
                                    if (P[i, j + countY + 1] == 0)
                                    {
                                        countY++;
                                    }
                                    else
                                    {
                                        break;
                                    }
                                }
                                else
                                {
                                    break;
                                }

                            }
                            //Fill hole if it is wide enough
                            if ((countY != 0) && (countY + 1 <= ChainLinkDistance))
                            {
                                for (int temp = 0; temp <= countY; temp++)
                                {
                                    P[i, j + temp] = 1;
                                }
                            }
                        }
                    }
                    //change second condition when changeing direction
                    //Vertical Horizontal
                    if ((P[i, j] == 1) && (i != FP_IMAGE_WIDTH - 1) && (i != 0) && (j != FP_IMAGE_HEIGHT - 1) && (j != 0))
                    {
                        if (P[i + 1, j + 1] == 0)
                        {
                            short countYX = 0; //1
                            //count Horizontal Hole
                            while ((j + countYX <= FP_IMAGE_HEIGHT - 1) && (i + countYX <= FP_IMAGE_WIDTH - 1) && (countYX <= ChainLinkDistance))
                            {
                                if (((j + countYX + 1) <= FP_IMAGE_HEIGHT - 1) && ((i + countYX + 1) <= FP_IMAGE_WIDTH - 1) && ((countYX + 1) <= ChainLinkDistance))
                                    if (P[i + countYX + 1, j + countYX + 1] == 0)
                                    {
                                        countYX++;
                                    }
                                    else
                                    {
                                        break;
                                    }

                                else
                                {
                                    break;
                                }
                            }
                            //Fill hole if it is wide enough
                            if ((countYX != 0) && (countYX + 1 <= ChainLinkDistance))
                            {
                                for (int temp = 0; temp <= countYX; temp++)
                                {
                                    P[i + temp, j + temp] = 1;
                                }
                            }
                        }
                    }
                }
            }
        }


        /// <summery>
        /// 
        /// ################################
        /// #     Extract Origin           #
        /// ################################
        /// 
        /// In future i want to use the gradients to classifie the finger print into the 5 different 
        /// catagories which are marked in the FP_CLASS.
        ///    
        /// This function still needs to improved and somtimes dosen't find the center of the finger print.
        ///    
        /// The principle in finding the centre is simple , just find the greatest change in the gradient
        /// bettween two lines and you have your centre.
        ///    
        /// To find the classification you have to find the average changes in gradients in the different 
        /// sectors (if you divided your picture in 4 using the fingerprint centre as the centre).You should 
        /// then classifie the fingerprint according to this.
        /// 
        /// <summery>

        private Point getFingerPrintOrigin()
        {
            //Point m_Point = new Point();
            //double gradcur = 0;
            //double gradprev = 0;
            //double gradchangebig = 0;
            //double gradchange = 0;

            //double graddistancebig = 0;
            //double graddistance = 0;

            //double prevx = 0;
            //double prevy = 0;

            //for (int j = 50; j <= FP_IMAGE_HEIGHT - 50; j++)
            //{
            //    for (int i = 50; i <= FP_IMAGE_WIDTH - 50; i++)
            //    {
            //        if (P[i, j] == 1)
            //        {
            //            //count surrounding pixels
            //            int tc = 0;
            //            int x1 = 0;
            //            int y1 = 0;
            //            int x2 = 0;
            //            int y2 = 0;
            //            //find surrounding 1s
            //            for (int m = -1 * FP_TEMPLATE_SEARCH_RADIUS; m <= FP_TEMPLATE_SEARCH_RADIUS; m++)
            //            {
            //                for (int n = -1 * FP_TEMPLATE_SEARCH_RADIUS; n <= FP_TEMPLATE_SEARCH_RADIUS; n++)
            //                {
            //                    if ((m == FP_TEMPLATE_SEARCH_RADIUS) || (m == (-1) * FP_TEMPLATE_SEARCH_RADIUS) || (n == FP_TEMPLATE_SEARCH_RADIUS) || (n == (-1) * FP_TEMPLATE_SEARCH_RADIUS))
            //                    {
            //                        if (P[i + m, j + n] == 1)
            //                        {
            //                            tc++;
            //                            if (tc == 1)
            //                            {
            //                                x1 = i + m;
            //                                y1 = j + n;
            //                            }
            //                            if (tc == 2)
            //                            {
            //                                x2 = i + m;
            //                                y2 = j + n;
            //                            }
            //                        }//end if
            //                    }//end if
            //                }//end for n
            //            } //end for m         
            //            //does all the hard work of finding the greatest change in gradient
            //            if (tc == 2)
            //            {
            //                if ((x2 - x1) > 0)
            //                {
            //                    gradcur = (y2 - y1) / (x2 - x1);
            //                    //check to see gradient change by at least 270 degrees
            //                    if ((gradcur > 0) && (gradprev < 0))
            //                    {
            //                        gradchange = Math.Abs(gradcur) + Math.Abs(gradprev);
            //                        graddistance = Math.Abs(i) - Math.Abs(prevx);
            //                        if (gradchangebig < gradchange)
            //                        {
            //                            if (graddistancebig < graddistance)
            //                            {
            //                                gradchangebig = gradchange;
            //                                graddistancebig = graddistance;
            //                                m_Point.X = i;//FP_ORIGIN_X =i;
            //                                m_Point.Y = j;//FP_ORIGIN_Y =j;
            //                            }
            //                        }
            //                        break;
            //                    }
            //                    //reset varibles for new checks
            //                    gradprev = gradcur;
            //                    gradcur = 0;
            //                    prevx = i;
            //                    prevy = j;
            //                }//(x2-x1)>0
            //            }//end if tc==2
            //        }//end if P[x,y]==1
            //    }//end for i
            //}//end for j
            ////  JOptionPane.showMessageDialog (null,Integer.toString(FP_ORIGIN_X)+";"+Integer.toString(FP_ORIGIN_Y),"getFingerPrintOrigin",JOptionPane.PLAIN_MESSAGE);
            ////Remove Nam
            //m_Point.X = 200;
            //m_Point.Y = 200;

            //return m_Point;
            return Core;
        }

        private int getFingerPrintClassification()
        {
            Point m_Point = this.getFingerPrintOrigin();
            double gradcur = 0;

            //stores total gradient of corners
            double gradlt = 0;
            double gradrt = 0;
            double gradlb = 0;
            double gradrb = 0;

            //counts total of each corner gradient
            double cgradlt = 0;
            double cgradrt = 0;
            double cgradlb = 0;
            double cgradrb = 0;

            for (int j = 50; j <= FP_IMAGE_HEIGHT - 50; j++)
            {
                for (int i = 50; i <= FP_IMAGE_WIDTH - 50; i++)
                {
                    if (P[i, j] == 1)
                    {
                        //count surrounding pixels
                        int tc = 0;
                        int x1 = 0;
                        int y1 = 0;
                        int x2 = 0;
                        int y2 = 0;
                        //find surrounding 1s
                        for (int m = -1 * FP_TEMPLATE_SEARCH_RADIUS; m <= FP_TEMPLATE_SEARCH_RADIUS; m++)
                        {
                            for (int n = -1 * FP_TEMPLATE_SEARCH_RADIUS; n <= FP_TEMPLATE_SEARCH_RADIUS; n++)
                            {
                                if ((m == FP_TEMPLATE_SEARCH_RADIUS) || (m == (-1) * FP_TEMPLATE_SEARCH_RADIUS) || (n == FP_TEMPLATE_SEARCH_RADIUS) || (n == (-1) * FP_TEMPLATE_SEARCH_RADIUS))
                                {
                                    if (P[i + m, j + n] == 1)
                                    {
                                        tc++;
                                        if (tc == 1)
                                        {
                                            x1 = i + m;
                                            y1 = j + n;
                                        }
                                        if (tc == 2)
                                        {
                                            x2 = i + m;
                                            y2 = j + n;
                                        }
                                    }//end if
                                }//end if
                            }//end for n
                        } //end for m         
                        //does all the hard work of finding the greatest change in gradient
                        if (tc == 2)
                        {
                            if ((x2 - x1) > 0)
                            {
                                gradcur = (y2 - y1) / (x2 - x1);
                                //check to see gradient change by at least 270 degrees
                                if ((x2 < m_Point.X) && (y2 > m_Point.Y))
                                {
                                    gradlt = gradlt + gradcur;
                                    gradlt++;
                                }
                                else if ((x2 > m_Point.X) && (y2 > m_Point.Y))
                                {
                                    gradrt = gradrt + gradcur;
                                    gradrt++;
                                }
                                else if ((x2 < m_Point.X) && (y2 < m_Point.Y))
                                {
                                    gradlb = gradlb + gradcur;
                                    gradlb++;
                                }
                                else if ((x2 > m_Point.X) && (y2 < m_Point.Y))
                                {
                                    gradrb = gradrb + gradcur;
                                    gradrb++;
                                }

                            }//(x2-x1)>0
                        }//end if tc==2
                    }//end if P[x,y]==1
                }//end for i
            }//end for j
            //get average gradient for 4 corners
            gradlb = gradlb / cgradlb;
            gradrb = gradrb / cgradrb;
            gradlt = gradlt / cgradlt;
            gradrt = gradrt / cgradrt;
            //determin classification according to gradient
            //needs work
            if ((gradlt > 0) && (gradrt > 0) && (gradlb > 0) && (gradrb > 0))
            {
                return FP_CLASS_WHORL;
            }
            else if ((gradlt > 0) && (gradrt > 0) && (gradlb > 0) && (gradrb > 0))
            {
                return FP_CLASS_LEFT_LOOP;
            }
            else if ((gradlt > 0) && (gradrt > 0) && (gradlb > 0) && (gradrb > 0))
            {
                return FP_CLASS_RIGHT_LOOP;
            }
            else if ((gradlt > 0) && (gradrt > 0) && (gradlb > 0) && (gradrb > 0))
            {
                return FP_CLASS_ARCH;
            }
            else if ((gradlt > 0) && (gradrt > 0) && (gradlb > 0) && (gradrb > 0))
            {
                return FP_CLASS_ARCH_TENTED;
            }
            else
            {
                return 1;
            }
            //  JOptionPane.showMessageDialog (null,Integer.toString(FP_ORIGIN_X)+";"+Integer.toString(FP_ORIGIN_Y),"getFingerPrintOrigin",JOptionPane.PLAIN_MESSAGE);
        }


        /// <summery>
        /// 
        /// ################################
        /// #     Extract Template         #
        /// ################################
        /// 
        /// The template will have to be formated according to the ISO standards as set out buy
        /// NIST , NIST also has a set of binary pictures to use for examples. This database is used for
        /// determaning the FAR(False Acceptance Rate) and FRR (False Rejection Rate)
        /// 
        /// First 7 are (elements in array , originx , originy , null , null , null ,null) after that
        /// the format is (x,y,r,degree ,number of ends,resultant degree).The size of the array 
        /// is always pre set.
        /// 
        /// There is also future work that needs to be done on genralization , basicly what this means is 
        /// that you take 3 finger templates , then take the features that are common to all three templeate
        /// and you will then come out with a generalized template.This will improve quality of the template.
        /// 
        ///	</summery>


        public double[] getFingerPrintTemplate()
        {
            // final int SEARCH_RADIUS = 1;   
            double x = 0;
            double y = 0;
            double r = 0;
            double d = 0;
            double[] m_arr = new double[FP_TEMPLATE_MAX_SIZE];

            this.ThinningHilditch();
            this.ThinningHitAndMiss();
            this.ThinningHilditch();
            this.ThinningHitAndMiss();

            Point origin = this.getFingerPrintOrigin();
            m_arr[1] = origin.X;
            m_arr[2] = origin.Y;
            //m_arr[1] = origin.X;
            //m_arr[2] = origin.Y;
            int c = 7;
            int previ = 0;
            int prevj = 0;

            bool first = true;

            //start from 5 units in to avoid detection of edges of finger print and out of bound exceptions
            for (int j = 5; j <= FP_IMAGE_HEIGHT - 6; j++)
            {
                first = true;
                for (int i = 5; i <= FP_IMAGE_WIDTH - 6; i++)
                {
                    if ((c < FP_TEMPLATE_MAX_SIZE) && (P[i, j] == 1) && (i != FP_IMAGE_WIDTH - 1) && (i != 0) && (j != FP_IMAGE_HEIGHT - 1) && (j != 0))
                    {
                        /*  
                        *   Must not capture first and last feature because those are the edges of the finger print
                        *   and will provide no value to the template.
                        */
                        if (first == true)
                        {
                            first = false;
                            //cheak to see if previos item in array was aslo end
                            if ((c > 7) && ((m_arr[c - 6] + origin.X) == previ) && ((m_arr[c - 5] + origin.Y) == prevj))
                            {
                                //delete previos featue
                                m_arr[c--] = 0;
                                m_arr[c--] = 0;
                                m_arr[c--] = 0;
                                m_arr[c--] = 0;
                                m_arr[c--] = 0;
                                m_arr[c--] = 0;
                            }
                        }
                        else
                        {
                            int tc = 0;
                            for (int m = -1 * FP_TEMPLATE_SEARCH_RADIUS; m <= FP_TEMPLATE_SEARCH_RADIUS; m++)
                            {
                                for (int n = -1 * FP_TEMPLATE_SEARCH_RADIUS; n <= FP_TEMPLATE_SEARCH_RADIUS; n++)
                                {
                                    if ((m == FP_TEMPLATE_SEARCH_RADIUS) || (m == (-1) * FP_TEMPLATE_SEARCH_RADIUS) || (n == FP_TEMPLATE_SEARCH_RADIUS) || (n == (-1) * FP_TEMPLATE_SEARCH_RADIUS))
                                    {
                                        if (P[i + m, j + n] == 1)
                                        {
                                            tc++;
                                        }//end if
                                    }//end if
                                }//end for n
                            } //end for m         

                            //calculate parameters necesary for template
                            if ((tc == 1) || (tc == 3))
                            {
                                x = i - origin.X;
                                y = j - origin.Y;
                                r = Math.Sqrt(x * x + y * y);
                                if ((x > 0) && (y > 0))
                                {
                                    d = Math.Atan(y / x);
                                }
                                else if ((x < 0) && (y > 0))
                                {
                                    d = Math.Atan(y / x) - Math.PI;
                                }
                                else if ((x < 0) && (y < 0))
                                {
                                    d = Math.PI + Math.Atan(y / x);
                                }
                                else if ((x > 0) && (y < 0))
                                {
                                    d = 2 * Math.PI + Math.Atan(y / x);
                                }
                            }

                            //check to see if point already been captured
                            bool foundx = false;
                            bool foundy = false;
                            for (int m = 7; m <= c; m = m + 6)
                            {
                                if (m_arr[m + 4] == 3)
                                {
                                    if (Math.Abs(Math.Abs((int)m_arr[m]) - Math.Abs(x)) < 4)
                                    {
                                        foundx = true;
                                    }
                                    if (Math.Abs(Math.Abs((int)m_arr[m + 1]) - Math.Abs(y)) < 4)
                                    {
                                        foundy = true;
                                    }
                                }//end if
                            }//end for m


                            //1 surrounding 1s
                            if ((tc == 1) && (c <= FP_TEMPLATE_MAX_SIZE - 6) && (x != 0) && (y != 0) && ((foundx == false) || (foundy == false)))
                            {

                                if (P[i - 1, j + 1] == 1)
                                {
                                    m_arr[c++] = x;
                                    m_arr[c++] = y;
                                    m_arr[c++] = r;
                                    m_arr[c++] = d;
                                    m_arr[c++] = 1;
                                    m_arr[c++] = 135;
                                }
                                else if (P[i, j + 1] == 1)
                                {
                                    m_arr[c++] = x;
                                    m_arr[c++] = y;
                                    m_arr[c++] = r;
                                    m_arr[c++] = d;
                                    m_arr[c++] = 1;
                                    m_arr[c++] = 90;
                                }
                                else if (P[i + 1, j + 1] == 1)
                                {
                                    m_arr[c++] = x;
                                    m_arr[c++] = y;
                                    m_arr[c++] = r;
                                    m_arr[c++] = d;
                                    m_arr[c++] = 1;
                                    m_arr[c++] = 45;
                                }
                                else if (P[i + 1, j] == 1)
                                {
                                    m_arr[c++] = x;
                                    m_arr[c++] = y;
                                    m_arr[c++] = r;
                                    m_arr[c++] = d;
                                    m_arr[c++] = 1;
                                    m_arr[c++] = 0;
                                }
                                else if (P[i + 1, j - 1] == 1)
                                {
                                    m_arr[c++] = x;
                                    m_arr[c++] = y;
                                    m_arr[c++] = r;
                                    m_arr[c++] = d;
                                    m_arr[c++] = 1;
                                    m_arr[c++] = 315;
                                }
                                else if (P[i, j - 1] == 1)
                                {
                                    m_arr[c++] = x;
                                    m_arr[c++] = y;
                                    m_arr[c++] = r;
                                    m_arr[c++] = d;
                                    m_arr[c++] = 1;
                                    m_arr[c++] = 270;
                                }
                                else if (P[i - 1, j - 1] == 1)
                                {
                                    m_arr[c++] = x;
                                    m_arr[c++] = y;
                                    m_arr[c++] = r;
                                    m_arr[c++] = d;
                                    m_arr[c++] = 1;
                                    m_arr[c++] = 225;
                                }
                                else if (P[i - 1, j] == 1)
                                {
                                    m_arr[c++] = x;
                                    m_arr[c++] = y;
                                    m_arr[c++] = r;
                                    m_arr[c++] = d;
                                    m_arr[c++] = 1;
                                    m_arr[c++] = 180;
                                }
                            }
                            else if ((tc >= 3) && (c <= FP_TEMPLATE_MAX_SIZE - 6) && (x != 0) && (y != 0) && ((foundx == false) || (foundy == false)))
                            {
                                //3 surrounding 1s
                                m_arr[c++] = x;
                                m_arr[c++] = y;
                                m_arr[c++] = r;
                                m_arr[c++] = d;
                                m_arr[c++] = 3;
                                m_arr[c++] = 0;
                            }//if tc>=3 
                        }//end if first
                        previ = i;
                        prevj = j;

                        //306;269
                        if (((i - origin.X) >= (306 - 4)) && ((i - origin.Y) >= (269 - 4)))
                        {
                            if (((i - origin.X) <= (306 + 4)) && ((i - origin.Y) <= (269 + 4)))
                            {
                                //JOptionPane.showMessageDialog (null,Double.toString(c)+";"+Integer.toString(i)+";"+Integer.toString(j),"My Point",JOptionPane.PLAIN_MESSAGE);
                            }
                        }

                    }//end if that checks for p[x,y]=1
                }//end for
            }//end for
            //put total size of points collected at 0 in array
            m_arr[0] = c;
            return m_arr;
        }//end getFingerPrintTemplate()

        public String ConvertFingerPrintTemplateDoubleToString(double[] finger)
        {
            String temp = "";
            for (int i = 0; i <= finger.Length - 1; i++)
            {
                temp = temp + finger[i].ToString() + ";";
            }
            return temp;
        }

        public double[] ConvertFingerPrintTemplateStringToDouble(String finger)
        {


            string[] fb = finger.Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
            double[] m_finger = new double[fb.Length];
            //int c = -1;
            //String m_double = "";
            //String temp = "";
            //for (int i = 0; i <= finger.Length - 1; i++)
            //{
            //    char[] tempch = finger.ToCharArray();
            //    temp = temp[i].ToString();
            //    if (temp == ";")
            //    {
            //        m_finger[c++] = Double.Parse(m_double);
            //    }
            //    else
            //    {
            //        m_double = m_double + temp;
            //    }
            //}
            int i = 0;
            foreach (string st in fb)
            {
                m_finger[i] = Double.Parse(st);
                i++;
            }
            return m_finger;
        }

        /// <summery>
        /// ################################
        /// #         Matching             #
        /// ################################
        /// 		    
        /// Something to possably look at are
        /// 		
        /// Distance = (X1 -X2)^2 + (Y1 - Y2)^2. The Error_Rating , if a image is is to the left or
        /// right or even at a angle the distance betwwen matched points will always be the same.
        /// 		    
        /// The matching dose account for rotation , thats what the cos and sin are for. 
        /// 		
        /// In matching you will never get a 100% match uunless they are exactly the same 
        /// image. 60% is quite good in general ,anything above 55% is considered a match , even 
        /// in other commercial versions. Remember you comparing twod DIFFERENT images then drawing a conclusion of a match.
        /// 
        /// </summery>

        //cross-corelation algorithm
        public int Match(double[] finger1, double[] finger2, int threshold, bool fastmatch)
        {
            //compare matrix with all shifted matrixes
            //must do later. must get the size of the array
            // JOptionPane.showMessageDialog (null,Double.toString(finger1[0])+";"+Double.toString(finger1[1])+";"+Double.toString(finger2[3]),"Match",JOptionPane.PLAIN_MESSAGE);
            double matchcount = 0;
            double matchcounttotal = (finger1[0] - 6) / 6;
            double bestmatch = 0;
            double radian = Math.PI / 180;
            bool foundpoint;

            for (int k = -1 * FP_MATCH_POINT_ROTATION_MOVEMENT; k <= FP_MATCH_POINT_ROTATION_MOVEMENT; k++)
            {
                for (int i = 7; i <= finger1[0] - 5; i = i + 6)
                {
                    foundpoint = false;
                    for (int j = 7; j <= finger2[0] - 5; j = j + 6)
                    {
                        if (foundpoint == false)
                        {
                            //compare two points account for rotational , verticle and horizontal shift
                            int resx = 0;
                            int resy = 0;
                            double x1 = 0;
                            double y1 = 0;
                            double x2 = 0;
                            double y2 = 0;
                            double r = 0;
                            double d = 0;
                            //find nessasary parameters

                            r = finger2[j + 2];
                            d = finger2[j + 3];
                            x2 = finger1[i];
                            y2 = finger1[i + 1];
                            //do angle shift for x
                            x1 = r * Math.Cos(d + (k * radian));
                            resx = Math.Abs((int)x2 + (int)(-1 * x1));
                            //do angle shift for y         
                            y1 = r * Math.Sin(d + (k * radian));
                            resy = Math.Abs((int)y2 + (int)(-1 * y1));

                            //cheak shift matchs count as match
                            if ((FP_MATCH_POINT_DISTANCE_MOVEMENT > resx) && (FP_MATCH_POINT_DISTANCE_MOVEMENT > resy))
                            {

                                //cheak if same kind of feature
                                if (finger1[i + 4] == finger2[j + 4])
                                {
                                    //cheak if feature in  same direction
                                    //  if(((finger1[i+5]-finger2[j+5])<=46)||((finger1[i+5]==0)&&(finger2[j+5]==315))||((finger1[i+5]==0)&&(finger2[j+5]==45)))
                                    //  {
                                    matchcount++;
                                    foundpoint = true;
                                    //break;
                                    //  }//cheak if feature in  same direction
                                } //cheak if same kind of feature

                            }//end if
                        }//if found
                    }//end for j
                }//end for i
                //see if we have a match

                if ((((matchcount / matchcounttotal) * 100) >= threshold) && (fastmatch == true))
                {
                    //found match
                    return (int)((matchcount / matchcounttotal) * 100);
                }
                else
                {
                    //not found match     
                    if (matchcount > bestmatch)
                    {
                        bestmatch = matchcount;
                    }
                    //reset match counter to 0
                    matchcount = 0;
                } //end if

            }//end for k
            return (int)((bestmatch / matchcounttotal) * 100);
        }//end Match

        private void FindSingularrity(int N1, int N2)
        {
            int[,] S1 = new int[512, 512];
            int[,] S2 = new int[512, 512];
            for (int i = 0; i < 512; i++)
            {
                for (int j = 0; j < 512; j++)
                {
                    S1[i, j] = 0;
                    S2[i, j] = 0;
                }
            }
            for (int i = N1; i < 512 - N1; i++)
            {
                for (int j = N1; j < 512 - N1; j++)
                {
                    if ((P[i - N1, j] == 0) || (P[i + N1, j] == 0) || (P[i, j - N1] == 0) || (P[i, j + N1] == 0))
                    {
                        int k = PoinCare(N1, i, j);
                        if (k == 360 || k == 180)
                        {
                            S1[i, j] = 1;
                        }
                    }
                }
            }
            for (int i = N2; i < 512 - N2; i++)
            {
                for (int j = N2; j < 512 - N2; j++)
                {
                    if ((P[i - N2, j] == 0) || (P[i + N2, j] == 0) || (P[i, j - N2] == 0) || (P[i, j + N2] == 0))
                    {
                        int k = PoinCare(N2, i, j);
                        if (k == -180)
                        {
                            S2[i, j] = 1;
                        }
                    }
                }
            }
            List<int> i_core;
            List<int> j_core;
            float core = 0.5f;
            do
            {
                i_core = new List<int>();
                j_core = new List<int>();
                for (int i = (N1 + 1) / 2 - 1; i < 512 - (N1 - 1) / 2; i++)
                {
                    for (int j = (N1 + 1) / 2 - 1; j < 512 - (N1 - 1) / 2; j++)
                    {
                        float tb = TrungBinh(S1, i - (N1 - 1) / 2, i + (N1 - 1) / 2, j - (N1 - 1) / 2, j + (N1 - 1) / 2);
                        if (tb >= core) //(tb >= core)
                        {
                            i_core.Add(i);
                            j_core.Add(j);
                        }
                    }
                }
                core = core - 0.05f;
            } while (core > 0.25 && i_core.Count == 0);
            int sumCorei = 0, sumCoreJ = 0;
            for (int i = 0; i < i_core.Count; i++)
            {
                sumCorei += i_core[i];
                sumCoreJ += j_core[i];
            }
            Core = new Point(sumCorei / i_core.Count, sumCoreJ / j_core.Count);
            //tim diem delta ben phai
            float delta = 0.4f;
            int i_delta = 0, j_delta = 0;
            do
            {
                for (int i = Core.X + (N2 + 1) / 2; i < 512 - (N2 - 1) / 1 + 1; i++)
                {
                    for (int j = Core.Y + (N2 + 1) / 2; j < 512 - (N2 - 1) / 1 + 1; j++)
                    {
                        float tb = TrungBinh(S2, i - (N2 - 1) / 2, i + (N2 - 1) / 2 + 1, j - (N2 - 1) / 2, j + (N2 - 1) / 2 + 1);
                        if (tb > delta)
                        {
                            i_delta = i; j_delta = j;
                            delta = tb;
                        }
                    }
                }
                delta -= 0.05f;
            } while (delta > 0.25 && i_delta == 0);
            int deltaR1 = 500, deltaR2 = 500;

            if (i_delta != 0 && j_delta != 0)
            {
                deltaR1 = i_delta - Core.X; deltaR2 = j_delta - Core.Y;
            }
            DeltaR = new Point(deltaR1, deltaR2);
            //ben trai
            delta = 0.4f;
            i_delta = j_delta = 0;
            do
            {
                for (int i = (N2 + 1) / 2 - 1; i < Core.X - (N2 - 1) / 2; i++)
                {
                    for (int j = Core.Y + (N2 + 1) / 2; j < 512 - (N2 - 1) / 1 + 1; j++)
                    {
                        float tb = TrungBinh(S2, i - (N2 - 1) / 2, i + (N2 - 1) / 2, j - (N2 - 1) / 2, j + (N2 - 1) / 2);
                        if (tb > delta)
                        {
                            i_delta = i; j_delta = j;
                            delta = tb;
                        }
                    }
                }
                delta -= 0.05f;
            } while (delta > 0.25 && i_delta == 0);
            int deltaL1 = -500, deltaL2 = 500;
            if (i_delta != 0 && j_delta != 0)
            {
                deltaL1 = i_delta - Core.X; deltaL2 = j_delta - Core.Y;
            }
            DeltaL = new Point(deltaL1, deltaL2);
        }
        private float TrungBinh(int[,] S, int iStart, int iEnd, int jStart, int jEnd)
        {
            int sum = 0;
            int count = 0;
            for (int i1 = iStart; i1 < iEnd; i1++)
            {
                for (int j1 = jStart; j1 < jEnd; j1++)
                {
                    sum += S[i1, j1];
                    count++;
                }
            }
            return (float)sum / (float)count;
        }
        private int PoinCare(int n, int i, int j)
        {
            int a = (n - 1) / 2;
            double d = 0;
            double d0 = Direct[i - a, j - a];
            double delta = 0;
            for (int k = j - a + 1; k < j + a + 1; k++)
            {
                delta = Direct[i - a, k] - d0;
                if (delta < -Math.PI / 2)
                {
                    delta = Math.PI + delta;
                }
                else if (delta > Math.PI / 2)
                {
                    delta = delta - Math.PI;
                }
                d = d + delta;
                d0 = Direct[i - a, k];
            }
            for (int h = i - a + 1; h < i + a + 1; h++)
            {
                delta = Direct[h, j + a] - d0;
                if (delta < -Math.PI / 2)
                {
                    delta = delta + Math.PI;
                }
                else if (delta > Math.PI / 2)
                {
                    delta = delta - Math.PI;
                }
                d = d + delta;
                d0 = Direct[h, j + a];
            }
            for (int k = j + a - 1; k > j - a - 1; k--)
            {
                delta = Direct[i + a, k] - d0;
                if (delta < -Math.PI / 2)
                {
                    delta = Math.PI + delta;
                }
                else if (delta > Math.PI / 2)
                {
                    delta = delta - Math.PI;
                }
                d = d + delta;
                d0 = Direct[i + a, k];
            }
            for (int h = i + a - 1; h > i - a - 1; h--)
            {
                delta = Direct[h, j - a] - d0;
                if (delta < -Math.PI / 2)
                {
                    delta = delta + Math.PI;
                }
                else if (delta > Math.PI / 2)
                {
                    delta = delta - Math.PI;
                }
                d = d + delta;
                d0 = Direct[h, j - a];
            }
            return (int)(d * 180 / Math.PI);
        }

    }//end class
}//end namespace